<html xmlns="http://www.w3.org/1999/xhtml" id="rpc">
<head>
    <title>OpenLaszlo RPC</title>
</head>

<body>

<h1>OpenLaszlo RPC</h1>
<warning>
Remote procedure calls work in proxied applications only.  They do not work in SOLO applications.
</warning>

<h2>Overview</h2>
<p><dfn>OpenLaszlo RPC</dfn>, or just <dfn>RPC</dfn>, is the general term used to
define the implementation and APIs that invoke remote procedures calls or
services over the network. Current RPC implementations are <tagname link="true">javarpc</tagname>, <tagname link="true">xmlrpc</tagname>, and
<tagname link="true">soap</tagname>.</p>
<p>
A related class, <classname link="true">XMLHTTPRequest</classname> (also called "AJAX API"), is described in <xref linkend="data-structures"/>.
</p>


<p><tagname>javarpc</tagname> allows server-side Java methods to be invoked from the
application. Java objects can be stored in the OpenLaszlo Server using an HTTP session (<a
href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/http/HttpSession.html">HttpSession</a>)
or the web application object (<a
href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/ServletContext.html">SevletContext</a>).
See the <a href="http://java.sun.com/products/servlet/2.3/javadoc/">Java Servlet
APIs</a> for more information. 
</p>
<p>
<tagname>xmlrpc</tagname> implements <a
href="http://www.xmlrpc.com">XML-RPC</a>, a simple spec that
allows applications to make remote procedure calls on different systems. Its
transport is HTTP and it uses an XML encoded message to invoke remote functions. </p>
<p>
<tagname>soap</tagname> implements the W3C <a
href="http://www.w3.org/TR/soap12-part0">SOAP
specification</a>. Like <tagname>xmlrpc</tagname> SOAO also uses XML to send messages over the network. Though HTTP
is commonly used as its transport, it isn't required. The specification is more
complex than XML-RPC and supports different styles of operations (rpc or
document), overloaded methods, passing header information, and so forth.</p>

<p>The <tagname link="true">rpc</tagname>tag is the abstract base class for all<dfn>RPC classes</dfn>.
Instances of RPC classes are referred to as <dfn>RPC objects</dfn>. RPC classes
include &lt;soap&gt;, &lt;javarpc&gt;, &lt;xmlrpc&gt;, &lt;sessionrpc&gt;, and
&lt;webapprpc&gt;. These classes are essentially LZX wrappers of LFC LzRPC
services. <classname>LzRPC</classname> is the LFC abstract class for LFC RPC services. This service
provides the basic framework to invoke remote functions over the network.
LzJavaRPCService, LzXMLRPCService, and LzSOAPService are subclasses of LzRPC,
and implement JavaRPC, XML-RPC, and SOAP, respectively.</p>

<!-- ============================================================ -->

<h2>&lt;rpc&gt;</h2>

<p>The &lt;rpc&gt; tag is the abstract base class for RPC classes. Subclasses
must implement the <method>load</method> method, which is responsible for creating the proxy
object. The <dfn>proxy</dfn> object contains a set of stub functions that invoke
a remote function (or procedure) over the network. It's up to the caller of the
stub function to know what parameters need to be passed in by looking at what
the backend remote function expects. 
</p>
<p>If you're calling a JavaRPC function, for example, you will need to look at 
the associated Java API. If you're calling a SOAP function, you will need to look at the corresponding operation in a WSDL file.</p>

<example extract="false">
&lt;rpc autoload="[true|false]"
     secure="[true|false]"
     secureport="..."&gt;
</example>

<p>The implementation of this class can be seen in
<tt>lps/components/rpc/rpc.lzx</tt>.</p>

<!-- ============================================================ -->

<h3>Attributes</h3>

<p><dfn>autoload</dfn>: (Boolean) if true, calls to load client proxy during
init stage. If false, the proxy must be loaded using the load() method.
<b>Default is true.</b></p>
<p><dfn>secure</dfn>: (Boolean) if true, creates a secure HTTPS connection
between the client and LPS. Also see secureport below. <b>Default is
false.</b></p>
<p><dfn>secureport</dfn>: (Number) valid only when secure attribute is set to
true. The secure port to use. There is no client-side default. Most servers use
port 443 as the default HTTPS port.</p>

<!-- ============================================================ -->

<h4>Read-only properties</h4>

<p><dfn>proxy</dfn>: (Object) this is the object containing function stubs. It
is created by calling <method>load</method> (which happens during init if autoload is
true).
</p>
<p>
<code>proxy</code> is equivalent to:</p>
<ul>
	<li>a class in JavaRPC</li>
	<li>a service in SOAP </li>
</ul>

<p>
<code>proxy</code>
function stubs are equivalent to:</p>
<ul>
	<li>class methods in JavaRPC</li>
	<li>operations in SOAP</li>
	<li>methods in XML-RPC. </li>
</ul>

<p>
Note that proxy is not defined until the <event>onload</event> event is
sent, thus, function stubs cannot be invoked until onload. Each function stub
requires two arguments: an array of parameters and delegate. You can unload it
(i.e., set to null) by calling the <method>unload</method> method. Go to the proxy section for
details.</p>

<!-- ============================================================ -->

<h3>Events</h3>

<p>Note: event handler methods must be declared in the body of &lt;rpc&gt;.
Attribute event handlers will not work.</p>

<p><dfn>onload</dfn>: this event is triggered when the proxy is returned
to the client. </p>

<p><dfn>onunload</dfn>: this event is triggered when the proxy is unloaded from
the client.</p>

<p><dfn>ondata</dfn>: this event is triggered when a declared &lt;remotecall&gt;
doesn't handle its ondata events. See the &lt;remotecall&gt; section for
details.</p>

<p><dfn>onerror</dfn>: this event is triggered if there was a problem loading or
unloading the stub, or if a declared &lt;remotecall&gt; didn't handle its
onerror event. See the &lt;remotecall&gt; section for details.</p>

<!-- ============================================================ -->

<h3>Methods</h3>

<!-- ============================================================ -->

<h4>load()</h4>

<p>The <method>load</method> method is abstract in this class. Each subclass must define this
method. <method>load</method> is responsible for setting up the proxy </p>

<p><b>load() implementation in javarpc.lzx</b></p>

<example extract="false">
&lt;method name="load"&gt;

    /* other code here */

    var opts = {
        loadoption: this.loadoption,
        params: this.createargs,
        classname: this.classname,
        oname: this.attributename,
        scope: this.scope
    }

    LzJavaRPCService.loadObject(this._loadDel, opts, this.secure, this.secureport);
&lt;/method&gt;
</example>

<p><b>load() implementation in soap.lzx</b></p>

<example extract="false">
&lt;method name="load"&gt;
    LzSOAPService.loadObject(this._loadDel,
        { wsdl: this.wsdl, service: this.service, port: this.port },
          this.secure, this.secureport);
&lt;/method&gt;
</example>

<p><b>load() implementation in xmlrpc.lzx</b></p>

<example extract="false">
&lt;method name="load"&gt;
    // Since there aren't any prototypes to load for XML-RPC services, we just
    // create proxy using declared calls.
    for (var cn in this.subnodes) {
        /* code to set up this.proxy */
    }

    this._loadDel.execute( 
        { status: 'ok', message: 'ok', 
          stub: this.proxy, stubinfo: null }
    );

&lt;/method&gt;
</example>

<p>There are several private, undocumented properties in &lt;rpc&gt;. But one
that implementors of subclasses should be made aware of is the delegate property
called _loadDel (note: an underscore prefix in a component variable indicates that it is
private). This delegate must be passed in to any lower-level LFC APIs or must be
called at the end of <method>load</method>, as is done in the xmlrpc.lzx implementation of
<method>load</method> (shown above). In turn, <code>_loadDel</code> calls the <method>_load</method> method (a private
method in &lt;rpc&gt;) that sets up the proxy, registers declared
&lt;remotecall&gt; nodes, and finally sends the onload event.</p>

<!-- ============================================================ -->

<h4>unload()</h4>

<p>This method unloads the proxy from the RPC object. Just like load(), this
method has a corresponding _unloadDel delegate and _unload handler method. By
default, unload() sets the proxy property to null and then an unload event is
sent. However, you can override this function in the cases where
server-side clean up is required, as is done with JavaRPC objects.</p>

<p><b>unload() implementation in javarpc.lzx</b></p>

<example extract="false">
&lt;method name="unload"&gt;
    /* some other code here */

    // clean up server-side code
    LzJavaRPCService.unloadObject(this._unloadDel, 
        {
          classname: this.classname, 
          oname: this.attributename, 
          scope: this.scope
        }, 
        this.secure, this.secureport);
&lt;/method&gt;
</example>

<p>See <tt>lps/components/rpc/javarpc.lzx</tt> for more details.</p>

<!-- ============================================================ -->

<h2>Declaring rpc</h2>

<p>Since rpc is an abstract class, SOAP, JavaRPC, and XML-RPC declarations will
be used for demonstration.</p>

<example extract="false">
&lt;!-- SOAP --&gt;
&lt;soap name="mySOAP" wsdl="..."&gt;
    &lt;method event="onload"&gt;
        Debug.write('soap proxy loaded:', this.proxy);
    &lt;/method&gt;
    &lt;method event="onunload"&gt;
        Debug.write('soap proxy unloaded:', this.proxy);
    &lt;/method&gt;
&lt;/soap&gt;

&lt;!-- JavaRPC --&gt;
&lt;javarpc name="myJavaRPC" classname="..." scope="..."&gt;
    &lt;method event="onload"&gt;
        Debug.write('javarpc proxy loaded:', this.proxy);
    &lt;/method&gt;
    &lt;method event="onunload"&gt;
        Debug.write('javarpc proxy unloaded:', this.proxy);
    &lt;/method&gt;
&lt;/javarpc&gt;

&lt;!-- XML-RPC --&gt;
&lt;xmlrpc name="myXMLRPC" service="..."&gt;
    &lt;method event="onload"&gt;
        Debug.write('xmlrpc proxy loaded:', this.proxy);
    &lt;/method&gt;
    &lt;method event="onunload"&gt;
        Debug.write('xmlrpc proxy unloaded:', this.proxy);
    &lt;/method&gt;
&lt;/xmlrpc&gt;
</example>

<!-- ============================================================ -->
<a name="proxy"/>
<h2>Proxy</h2>

<p>The proxy property is an object that contains function stubs. It's set when
the load call returns. Each function represents a client-side stub to a remote
function or operation. Each function requires two arguments: an array of
parameters and a delegate. The order of parameters in the array should
match the parameters the backend operation expects. The delegate is required
because RPC calls are asynchronous, that is, there is no way to know when the
function call will return from the backend. When the response is received, the delegate calls the appropriate method.</p>
<a name="delegate-as-attribute"/>
<example extract="false">
&lt;!-- Assume mySOAP.proxy contains a function stub called someFunction() --&gt;
&lt;soap name="mySOAP" wsdl="..."&gt;
    &lt;method event="onload"&gt;
        Debug.write('soap proxy loaded:');
        Debug.inspect(this.proxy);
    &lt;/method&gt;
    &lt;method event="onunload"&gt;
        Debug.write('soap proxy unloaded:', this.proxy);
    &lt;/method&gt;
&lt;/soap&gt;

&lt;button text="clickme"&gt;
    &lt;attribute name="mydel" type="expression"
               value="$once{new LzDelegate(this, 'handler')}"

    &lt;method event="onclick"&gt;
        mySOAP.proxy.someFunction([p1, p2, ..., pN], this.myDel);
    &lt;/method&gt;

    &lt;method name="handler" args="retObj"&gt;
        Debug.write('RPC call returned', retObj);
    &lt;/method&gt;

&lt;/button&gt;
</example>


<!-- ============================================================ -->

<h3>RPC call return object</h3>

<p>When a remote call returns, an object is returned into the callback handler
that is specified in the delegate passed in the function stub call. The object
contains information relevant to the call and will be referred to as the
<dfn>return object</dfn>. Successful return objects contain <dfn>return
values</dfn>, which is the actual value returned from the RPC call. The return
value can be a simple type, array, or object. A successful return object looks
like:</p>

<example extract="false">
{
  status: 'ok',
  message: 'ok',
  data: DATA,
  opinfo: OPINFO
}
</example>

<p>Data is the return value from the RPC call. Opinfo is information specific to
the operation. For SOAP, this will contain the operation name, operation style,
and SOAP response headers. For JavaRPC, will contain the remote class name,
method name, attribute name of where this object was saved, and other values
specific to this call. For XML-RPC, opinfo will contain service URL and method
name.</p>

<p>A successful call can also return a void return type:</p>

<example extract="false">
{
  status: 'ok',
  message: 'void', 
  data: LzRPC.t_void, 
  opinfo: OPINFO 
}
</example>

<p>LzRPC.t_void is an object that represent a void return type.</p>

<p>A bad call returns an error object:</p>

<example extract="false">
{
  status: 'error', 
  errortype: ERROR_TYPE, 
  message: ERROR_MESSAGE, 
  error: ERROR_OBJECT,
  opinfo: OPINFO
}
</example>

<p>Message is a one sentence description of the error. The error property exists
only for exception and fault error types. There are four types of errors:</p>
<ul>
	<li>fault</li>
	<li>exception</li>
	<li>timeout</li>
	<li>servererror</li>
</ul>
 
<p>A <dfn>fault</dfn> error type indicates that
there was a problem handling the remote function call. An <dfn>exception</dfn>
error type is only thrown by SOAP calls and indicates the OpenLaszlo Server had a problem
handling the remote function call. Both fault and exception return an error
object that can be inspected by looking at the error property. A
<dfn>servererror</dfn> is returned for general OpenLaszlo Server errors like forbidden
requests. <dfn>Timeout</dfn> is returned when the application hasn't heard from
back from the OpenLaszlo Server in a certain amount of time. This currently defaults to 30
seconds and can't be changed.</p>

<example title="Return object">
&lt;canvas debug="true" width="600"&gt;

    &lt;debug x="150" y="20" width="400" height="360" /&gt;

    &lt;soap name="temperature" 
          wsdl="http://developerdays.com/cgi-bin/tempconverter.exe/wsdl/ITempConverter"&gt;

        &lt;attribute name="myDel" value="$once{new LzDelegate(this, 'myhandler')}" /&gt;

        &lt;method name="init"&gt;
            Debug.write('soap service loading...');
            super.init();
        &lt;/method&gt;

        &lt;method event="onload"&gt;
            Debug.write('temperature service loaded!');
            Debug.inspect(this.proxy);
        &lt;/method&gt;

        &lt;method name="myhandler" args="returnObject"&gt;
            Debug.write('got returned object:');
            Debug.inspect(returnObject);
        &lt;/method&gt;

    &lt;/soap&gt;

    &lt;view layout="spacing: 5" x="20" y="20"&gt;
        &lt;button text="ok conversion"&gt;
            &lt;method event="onclick"&gt;
                Debug.write('requesting good conversion...');
                temperature.proxy.FtoC([ 100 ], temperature.myDel);
            &lt;/method&gt;
        &lt;/button&gt;
        &lt;button text="bad conversion"&gt;
            &lt;method event="onclick"&gt;
                Debug.write('requesting bad conversion...');
                temperature.proxy.FtoC([ 'string' ], temperature.myDel);
            &lt;/method&gt;
        &lt;/button&gt;
    &lt;/view&gt;

&lt;/canvas&gt;
</example>

<!-- ============================================================ -->

<h3>Generating LzDataElement from return values</h3>

<p>Return values can be mapped to datasets or LzDataElements. This creates a
convenient way to bind return values from remote calls zto elements in the OpenLaszlo canvas. <dfn>Data mapped
return values</dfn> can be generated using the <method>LzDataElement.valueToElement</method>
method.</p>

<example title="LzDataElement.valueToElement()">
&lt;canvas debug="true" width="600"&gt;

    &lt;debug x="150" y="15" width="400" height="360" /&gt;

    &lt;simplelayout spacing="5" /&gt;

    &lt;method name="v2e" args="v"&gt;
        Debug.write('Got', v);
        var de = LzDataElement.valueToElement(v);
        Debug.write(de.serialize());
    &lt;/method&gt;

    &lt;button text="number"&gt;
        &lt;method event="onclick"&gt;
            var num = 5;
            canvas.v2e(num);
        &lt;/method&gt;
    &lt;/button&gt;

    &lt;button text="string"&gt;
        &lt;method event="onclick"&gt;
            var str = "a string";
            canvas.v2e(str);
        &lt;/method&gt;
    &lt;/button&gt;

    &lt;button text="array"&gt;
        &lt;method event="onclick"&gt;
            var arr = [1, 2, 3];
            canvas.v2e(arr);
        &lt;/method&gt;
    &lt;/button&gt;

    &lt;button text="object"&gt;
        &lt;method event="onclick"&gt;
            var obj = { p1: "a string", p2: 5 }
            canvas.v2e(obj);
        &lt;/method&gt;
    &lt;/button&gt;

    &lt;button text="complex array"&gt;
        &lt;method event="onclick"&gt;
            var arr = [ 1, { p1: "a string", p2: 5 }, [ 1, 2, 3] ];
            canvas.v2e(arr);
        &lt;/method&gt;
    &lt;/button&gt;

    &lt;button text="complex object"&gt;
        &lt;method event="onclick"&gt;
            var obj = { p1: [1, 2, 3], p2: 5 };
            canvas.v2e(obj);
        &lt;/method&gt;
    &lt;/button&gt;

&lt;/canvas&gt;
</example>

<p>The root node of <method>LzDataElement.valueToElement</method> is named
&lt;element&gt;. Simple type values are placed as text nodes of the root node.
Array items are placed as &lt;item&gt; nodes under the root. Object properties
are placed under the root with the name of the property representing the
element that wraps its value.</p> 

<p>In the example above, the number</p>
<pre>
5 
</pre>
<p>
maps to</p>
<pre> &lt;element&gt;5&lt;/element&gt;</pre>
<p>
The string
</p>
<pre>
"a string" 
</pre>
<p>
maps to</p>
<pre> &lt;element&gt;a string&lt;/element&gt;</pre>

<p> the array</p>
<pre>
[1, 2, 3]
</pre>
<p>
maps to</p>
<pre>
&lt;element&gt;
    &lt;item&gt;1&lt;/item&gt;
    &lt;item&gt;2&lt;/item&gt;
    &lt;item&gt;3&lt;/item&gt;
&lt;/element&gt;
</pre>
<p>the object</p>
<pre>
{ p1: "a string", p2: 5 }
</pre>
<p>
maps to</p>
<pre>
&lt;element&gt;
    &lt;p1&gt;a string&lt;/p1&gt;
    &lt;p2&gt;5&lt;/p2&gt;
&lt;/element&gt;
</pre>

<h4>SOAP calls and LzDataElements</h4>
<p>Note that returned arrays from SOAP calls are treated a little
differently. Each item in a SOAP array has a wrapper element, which is remapped
back when turned into an <classname>LzDataElement</classname>. That is, instead of wrapping array items
in &lt;item&gt;, they're wrapped with the original element name specified in the
SOAP message.</p>

<p>For example, assume the original SOAP message for an array return value looks like:</p>

<example extract="false">
&lt;soap:Envelope soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ...&gt;
   &lt;soap:Body&gt; &lt;n:getTypesResponse xmlns:n="http://arcweb.esri.com/v2"&gt;
         &lt;Result href="#id0" /&gt;
      &lt;/n:getTypesResponse&gt;
      &lt;id0 id="id0" soapenc:root="0" xsi:type="soapenc:Array" soapenc:arrayType="ns5:KeyValue[19]"&gt;
         &lt;i href="#id1" /&gt;
         &lt;i href="#id2" /&gt;
         &lt;i href="#id3" /&gt;
         &lt;i href="#id4" /&gt;
         &lt;!-- ... --&gt;
      &lt;/id0&gt;
      &lt;id1 id="id1" soapenc:root="0" xsi:type="ns5:KeyValue"&gt;
         &lt;key xsi:type="xsd:string"&gt;A&lt;/key&gt;
         &lt;value xsi:type="xsd:string"&gt;Countries&lt;/value&gt;
      &lt;/id1&gt;
      &lt;id2 id="id2" soapenc:root="0" xsi:type="ns5:KeyValue"&gt;
         &lt;key xsi:type="xsd:string"&gt;B&lt;/key&gt;
         &lt;value xsi:type="xsd:string"&gt;Large Non-U.S. Cities&lt;/value&gt;
      &lt;/id2&gt;
      &lt;id3 id="id3" soapenc:root="0" xsi:type="ns5:KeyValue"&gt;
         &lt;key xsi:type="xsd:string"&gt;C&lt;/key&gt;
         &lt;value xsi:type="xsd:string"&gt;Large U.S. Cities&lt;/value&gt;
      &lt;/id3&gt;
      &lt;!-- ... --&gt;

   &lt;/soap:Body&gt;
&lt;/soap:Envelope&gt;
</example>

<p>This example shows that &lt;item&gt; is not used as the wrapper element for
array items.</p>

<example title="SOAP array">
&lt;canvas debug="true" height="300"&gt;

    &lt;debug width="470" height="285" /&gt;

    &lt;soap name="geography" 
          wsdl="http://arcweb.esri.com/services/v2/PlaceFinderSample.wsdl"&gt;

        &lt;attribute name="myDel" value="$once{new LzDelegate(this, 'handler')}" /&gt;

        &lt;method name="handler" args="retval"&gt;
            if (retval.status == 'error') {
                Debug.write('error:', retval.message);
                return;
            }

            Debug.write('Got', retval.data);
            var el = LzDataElement.valueToElement(retval.data);
            Debug.write(el.serialize());
        &lt;/method&gt;

        &lt;method event="onload"&gt;
            Debug.write('geography soap service stub loaded');
            this.proxy.getTypes([], this.myDel);
        &lt;/method&gt;

    &lt;/soap&gt;

&lt;/canvas&gt;
</example>

<p>The data-mapped return value can then be used with another <classname>LzDataElement</classname> for
databinding purposes.</p>

<example title="Setting generated return value LzDataElement in dataset">
&lt;canvas debug="true" height="150"&gt;

    &lt;debug x="80" y="10" width="400" height="120" /&gt;

    &lt;dataset name="myDataset" /&gt;

    &lt;simplelayout spacing="5" /&gt;

    &lt;method name="v2e" args="v"&gt;
        Debug.write('setting', v, 'to mydataset');
        var de = LzDataElement.valueToElement(v);
        myDataset.setChildNodes( de.childNodes )
        Debug.write(myDataset.serialize());
    &lt;/method&gt;

    &lt;view datapath="myDataset:/" x="10" layout="inset: 10; spacing: 2"&gt;
        &lt;button text="array"&gt;
            &lt;method event="onclick"&gt;
                var arr = [1, 2, 3];
                canvas.v2e(arr);
            &lt;/method&gt;
        &lt;/button&gt;

        &lt;text datapath="item/text()" /&gt;
    &lt;/view&gt;

&lt;/canvas&gt;
</example>



<!-- ============================================================ -->

<h3>Using dataobject to map return values</h3>

<p>There is more convenient way to generate data mapped return values. Instead
of calling LzDataElement.valueToElement() after each call, a <code>dataobject</code> property
can be set in the delegate. <dfn>Dataobject</dfn> must be a dataset or an
LzDataElement and tells the RPC function to create a data mapped return value
and set it on the dataobject. If the dataobject is a dataset, the child nodes of
the data mapped return value are set as the child nodes of the dataset. If the
dataobject is an LzDataElement, the root node of data mapped return value is
appended as a child of the LzDataElement dataobject.</p>

<example title="Setting dataobject to a dataset">
&lt;canvas debug="true" width="700"&gt;

    &lt;debug x="220" width="470" height="285" /&gt;

    &lt;dataset name="myDataset" /&gt;

    &lt;soap name="geography" 
          wsdl="http://arcweb.esri.com/services/v2/PlaceFinderSample.wsdl"&gt;

        &lt;attribute name="myDel" value="$once{new LzDelegate(this, 'handler')}" /&gt;

        &lt;method name="handler" args="ret"&gt;
            Debug.write('ret:', ret);
            if (ret.status == 'ok') {
                Debug.write('myDataset childNodes:', myDataset.childNodes);
            }
        &lt;/method&gt;

        &lt;method event="onload"&gt;
            Debug.write('geography soap service stub loaded');

            // Here we set data
            this.myDel.dataobject = myDataset;

            this.proxy.getTypes([], this.myDel);
        &lt;/method&gt;

    &lt;/soap&gt;


    &lt;view datapath="myDataset:/" x="10" layout="inset: 10; spacing: 2"&gt;
        &lt;view datapath="i"&gt;
            &lt;text datapath="key/text()" resize="true"/&gt;
            &lt;text x="20" datapath="value/text()" resize="true"/&gt;
        &lt;/view&gt;
    &lt;/view&gt;

&lt;/canvas&gt;
</example>

<p>Note how the passed in LzDataElement child nodes are the child nodes of the
dataset. Here's another example using an LzDataElement.</p>

<example title="Setting dataobject to an LzDataElement">
&lt;canvas debug="true" height="500" width="700"&gt;

    &lt;debug x="220" width="470" height="285" /&gt;

    &lt;dataset name="myDataset"&gt;
        &lt;region name="Region-A" /&gt;
        &lt;region name="Region-B"/&gt;
        &lt;region name="Region-C"/&gt;
    &lt;/dataset&gt;

    &lt;soap name="placefinder" 
          wsdl="http://arcweb.esri.com/services/v2/PlaceFinderSample.wsdl"&gt;

        &lt;attribute name="myDel" value="$once{new LzDelegate(this, 'handler')}" /&gt;

        &lt;method name="handler" args="ret"&gt;
            Debug.write('ret:', ret);
            if (ret.status == 'ok') {
                Debug.write('myDataset childNodes:', myDataset.childNodes);
            }
        &lt;/method&gt;

        &lt;method event="onload"&gt;
            Debug.write('placefinder soap service stub loaded');
            myView.setAttribute('visible', true);
            this.proxy.getTypes([], this.myDel);
        &lt;/method&gt;

    &lt;/soap&gt;

    &lt;view name="myView" datapath="myDataset:/" x="10" visible="false"&gt;

        &lt;simplelayout spacing="2" inset="10" /&gt;

        &lt;view datapath="region" layout="spacing: 5"&gt;
            &lt;attribute name="text" value="$path{'@name'}" type="string" /&gt;

            &lt;text name="t" text="${parent.text + ' (click me)'}" resize="true" /&gt;

            &lt;method name="gotData"&gt;
                this.setAttribute('clickable', false);
                this.t.setText(this.text);
            &lt;/method&gt;

            &lt;method event="onclick"&gt;
                if (text == "Region-A") {
                    this.regions.setDatapath("element/i[1-6]");
                } else if (text == "Region-B") {
                    this.regions.setDatapath("element/i[7-12]");
                } else {
                    this.regions.setDatapath("element/i[12-]");
                }
                var myDel = new LzDelegate(this, "gotData");
                myDel.dataobject = this.datapath.p;
                placefinder.proxy.getTypes([], myDel);
            &lt;/method&gt;

            &lt;view name="regions" x="10"&gt;
                &lt;text datapath="key/text()" resize="true"/&gt;
                &lt;text x="20" datapath="value/text()" resize="true"/&gt;
            &lt;/view&gt;
        &lt;/view&gt;
    &lt;/view&gt;

&lt;/canvas&gt;
</example>

<p>Unlike a dataset, the generated LzDataElement from the return value is
appended as a child of the LzDataElement dataobject.</p>

<!-- ============================================================ -->

<h2>&lt;remotecall&gt;</h2>

<p>In the previous section we discussed how to make RPC calls using function
stubs defined in the proxy. Though not discouraged, calling function stubs
directly will generally result in your code looking very "scripty". The
<tagname link="true">remotecall</tagname> tag allows for a more declarative style approach to using RPC
functions.</p>

<example extract="false">
&lt;remotecall funcname="..."
     name="..."
     dataobject="..."
     remotecontext="..."&gt;
</example>

<h3>Attributes</h3>
<p>
The <tagname>remotecall</tagname> tag has the following attributes:
</p>

<p><dfn>funcname</dfn>: (String) the name of the function stub remotecall
represents. <b>This is required.</b></p>

<p><dfn>name</dfn>: (String) the name of the remotecall. Multiple remotecalls
can refer to the same function stub, but names must be unique. <b>Default is
value of funcname.</b></p>

<p><dfn>dataobject</dfn>: (Dataset|LzDataElement) if set, the return value will
also be represented as a dataset or as a child of the LzDataElement. <b>Default
is null.</b></p>

<p><dfn>remotecontext</dfn>: (Object) this attribute is used when the remotecall
isn't declared inside of an RPC object. This attribute should be set to the RPC
context the remotecall should run from. <b>Default is null.</b></p>


<h3>Events</h3>

<p><dfn>ondata</dfn>: this event is triggered when the remotecall successfully
returns data. If ondata is not handled in the remotecall, ondata cascades up to
its parent. If the parent doesn't handle it, it then cascades up to the
remotecontext, if defined. If none of these objects handle the ondata event, the returned data is ignored. The ondata event
sends two arguments to its event handler. The first is the actual return value,
the second is information on the operation that returned the value.</p>

<p><dfn>onerror</dfn>: this event is triggered when the remotecall returns an
error. If an onerror is not handled in the remotecall, onerror cascades up to
its parent. If the parent doesn't handle it, it then cascades up to the
remotecontext, if defined. If none of these objects handle the onerror event, the error is displayed in the debugger. The
onerror event sends three arguments to its event handler: the error message, an
error object (which can be null or undefined depending on the error type), and
information on the operation that originated the failed return.</p>

<h3>Methods</h3>

<h4>invoke(params, delegate)</h4>

<p><dfn>params</dfn>: (Array) an array of parameters to pass to the
remotecall. If null, the remotecall will use &lt;param&gt; tag values declared
inside of it. <b>Default is null</b>.</p>
<p><dfn>delegate</dfn>: (LzDelegate) a delegate to handle the callback. If null,
the remotecall will use the default delegate which calls the default handler,
whose job is to receive data and raise ondata/onerror events. <b>Default is
null</b>.</p>

<p>Call this method to invoke remotecall.</p>

<!-- ============================================================ -->

<h3>Using remotecall</h3>

<p>Use the invoke() method to use remotecall. If the parameter array (the first
argument) is null, declared &lt;param&gt; are used as arguments. A &lt;param&gt;
can set either a value attribute or define a getValue() method in its body. The
getValue() method is expected to return a value. If both value and getValue()
are defined, getValue() always wins. Note that the value attribute is an
expression type so strings must be quoted like <code>value="'my
string'"</code>. To set null, use the <code>value="$once{null}"</code>
syntax.</p>

<p>Function stubs can be referenced by two different remotecalls with different
parameters. The funcname attribute doesn't have to be unique, but if referenced
more than once, remotecalls must explicitly define their name attributes.</p>

<example title="Remotecall">
&lt;canvas debug="true" height="300" width="600"&gt;

    &lt;debug x="145" width="450" height="280" /&gt;

    &lt;soap name="google" wsdl="http://api.google.com/GoogleSearch.wsdl"&gt;

        &lt;method event="onload"&gt;
            Debug.write('google soap service stub loaded');
        &lt;/method&gt;

        &lt;method event="onerror" args="error"&gt;
            debug.write('error:', error);
        &lt;/method&gt;

        &lt;remotecall name="search" funcname="doGoogleSearch"&gt;

            &lt;param value="'2TKUw4ZQFHJ84ByemZK0EXV0Lj+7xGOx'" /&gt;
            &lt;param value="'sweet'" /&gt;
            &lt;param value="1" /&gt;
            &lt;param value="10" /&gt;
            &lt;param value="true" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="true" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="''" /&gt;

            &lt;method event="ondata" args="value"&gt;
                debug.write('result is:')
                debug.inspect(value);
            &lt;/method&gt;

        &lt;/remotecall&gt;

        &lt;remotecall name="togglesearch" funcname="doGoogleSearch"&gt;

            &lt;param value="'2TKUw4ZQFHJ84ByemZK0EXV0Lj+7xGOx'" /&gt;
            &lt;param&gt;
                &lt;attribute name="toggle" value="0" type="number" /&gt;
                &lt;method name="getValue"&gt;
                    var searchTerm;
                    if (toggle % 2 == 0) {
                        searchTerm = 'democrat';
                        toggle = 1;
                    } else {
                        searchTerm = 'republican';
                        toggle = 0;
                    }
                    Debug.write('search term is', searchTerm);
                    return searchTerm;
                &lt;/method&gt;
            &lt;/param&gt;
            &lt;param value="1" /&gt;
            &lt;param value="10" /&gt;
            &lt;param value="true" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="true" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="''" /&gt;

            &lt;method event="ondata" args="value"&gt;
                debug.write('result is:')
                debug.inspect(value);
            &lt;/method&gt;

        &lt;/remotecall&gt;
    &lt;/soap&gt;

    &lt;simplelayout spacing="10" /&gt;

    &lt;button x="10" y="10" text="search"&gt;
        &lt;method event="onclick"&gt;
            Debug.write('invoking search...');
            canvas.google.search.invoke();
        &lt;/method&gt;
    &lt;/button&gt;

    &lt;button x="10" y="10" text="toggle search"&gt;
        &lt;method event="onclick"&gt;
            Debug.write('invoking togglesearch...');
            canvas.google.togglesearch.invoke();
        &lt;/method&gt;
    &lt;/button&gt;

&lt;/canvas&gt;
</example>

<!-- ============================================================ -->

<h4>Using dataobject with remotecall</h4>

<p>Just like passing in a delegate with a dataobject property as discussed
previously with function stubs, a dataobject can be used in a remotecall by
setting the dataobject attribute.</p>

<example title="Setting dataobject in remotecall">
&lt;canvas debug="true" height="300" width="680"&gt;

    &lt;debug x="225" width="450" height="280" /&gt;

    &lt;dataset name="googleDset" /&gt;

    &lt;soap name="google" wsdl="http://api.google.com/GoogleSearch.wsdl"&gt;
        &lt;method event="onload"&gt;
            Debug.write('google soap service stub loaded');
        &lt;/method&gt;

        &lt;method event="onerror" args="error"&gt;
            debug.write('error:', error);
        &lt;/method&gt;

        &lt;remotecall name="search" funcname="doGoogleSearch" 
                    dataobject="googleDset"&gt;

            &lt;param value="'2TKUw4ZQFHJ84ByemZK0EXV0Lj+7xGOx'" /&gt;
            &lt;param value="'sweet'" /&gt;
            &lt;param value="1" /&gt;
            &lt;param value="10" /&gt;
            &lt;param value="true" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="true" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="''" /&gt;

            &lt;method event="ondata" args="value"&gt;
                debug.write('got result');
                debug.inspect(value);
            &lt;/method&gt;

        &lt;/remotecall&gt;
    &lt;/soap&gt;

    &lt;view layout="spacing: 5" &gt;
        &lt;button text="search" onclick="google.search.invoke()" /&gt;
        &lt;view bgcolor="yellow" layout="axis: y" &gt;
            &lt;view&gt;
                &lt;datapath xpath="googleDset:/resultElements/item" pooling="true" /&gt;
                &lt;text datapath="URL/text()" resize="true"/&gt;
            &lt;/view&gt;
        &lt;/view&gt;
    &lt;/view&gt;
&lt;/canvas&gt;
</example>

<!-- ============================================================ -->

<h4>Remotecall remotecontext</h4>

<p>In previous examples, a button was used to invoke a remotecall that was
declared in an RPC object. The code for a remotecall might have read better if
it was placed in the location where it's actually being used. Remotecall can
actually be declared anywhere in the node hierarchy, but when doing so, not only
must it know the function stub that it will trigger (i.e., the funcname), it
also must know which RPC context it will run from. To do so, the remotecontext
attribute must be set when declaring a remotecall outside of an RPC object.</p>

<example title="Remotecontext">
&lt;canvas debug="true" height="300" width="680"&gt;

    &lt;debug x="100" width="450" height="280" /&gt;

    &lt;soap name="google" wsdl="http://api.google.com/GoogleSearch.wsdl"&gt;
        &lt;method event="onload"&gt;
            Debug.write('google soap service stub loaded');
        &lt;/method&gt;
    &lt;/soap&gt;

    &lt;button text="search" onclick="this.doGoogleSearch.invoke()"&gt;

        &lt;!-- if name isn't set, name defaults to value of funcname. --&gt;

        &lt;remotecall funcname="doGoogleSearch" 
                    remotecontext="$once{canvas.google}"&gt;

            &lt;param value="'2TKUw4ZQFHJ84ByemZK0EXV0Lj+7xGOx'" /&gt;
            &lt;param value="'sweet'" /&gt;
            &lt;param value="1" /&gt;
            &lt;param value="10" /&gt;
            &lt;param value="true" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="true" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="''" /&gt;
            &lt;param value="''" /&gt;

            &lt;method event="ondata" args="value"&gt;
                debug.write('got result');
                debug.inspect(value);
            &lt;/method&gt;
            &lt;method event="onerror" args="error"&gt;
                debug.write('error:', error);
            &lt;/method&gt;
        &lt;/remotecall&gt;
    &lt;/button&gt;
&lt;/canvas&gt;
</example>

</body>
</html>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2001-2004 Laszlo Systems, Inc.  All Rights Reserved.              *
* Use is subject to license terms.                                            *
* X_LZ_COPYRIGHT_END ****************************************************** -->
